接著要來給 Todo 加上與 User 的關聯,區分各 User 建立的 Todo。
一個 User 擁有多個 Todo ,所以是一對多的關聯。
跟前面一樣先建立 migration 幫 Todo 加上 user_id 欄位。
sail artisan make:migration update_todo_relate_user --table todos
/database/migrations/2021_10_01_105954_update_todo_relate_user.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UpdateTodoRelateUser extends Migration
{
public function up()
{
Schema::table('todos', function (Blueprint $table) {
// 加上 user_id
$table->unsignedBigInteger('user_id');
});
}
public function down()
{
Schema::table('todos', function (Blueprint $table) {
$table->dropColumn('user_id');
});
}
}
接著在 User Model 中加入關聯
/app/Models/User.php
@@ -9,6 +9,7 @@ use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\UserSetting;
+use App\Models\Todo;
class User extends Authenticatable
{
@@ -50,4 +51,9 @@ class User extends Authenticatable
{
return $this->hasOne(UserSetting::class);
}
+
+ public function todos()
+ {
+ return $this->hasMany(Todo::class);
+ }
}
寫法跟一對一關聯相同,只是改成 hasMany 方法,簡單明瞭。
如果想要更改關聯欄位名稱,寫法與 hasOne 相同。
hasMany(<模型名稱>,<目標模型的外鍵名稱>,<模型關聯鍵名稱>)
至於從 Todo 反查詢一對多關聯的方法一樣是 belongsTo
/app/Models/Todo.php
@@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use App\Models\User;
class Todo extends Model
{
@@ -12,4 +13,9 @@ class Todo extends Model
protected $fillable = [
'name',
];
+
+ public function user()
+ {
+ return $this->belongsTo(User::class);
+ }
}
接著要讓 Todo 資料在新增的同時歸屬於 User ,要改寫 TodoController 的 store 方法。
/app/Http/Controllers/TodoController.php
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Todo;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
class TodoController extends Controller
{
@@ -39,13 +40,15 @@ class TodoController extends Controller
*/
public function store(Request $request)
{
$data = $request->all();
$todo = new Todo;
$todo->name = $data['name'];
- $todo->save();
+ $user = Auth::user();
+ $user->todos()->save($todo);
}
原本 $todo->save() 的方法改為先用 $user->todos() 建立關聯的 Query Builder 後再 save ,這樣新建的 todo 就會自動帶入 user_id 的資料。
也可以直接用 create() 建立,都是 Query Builder 的應用可以視情況使用。
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Todo;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
class TodoController extends Controller
{
@@ -39,13 +40,12 @@ class TodoController extends Controller
*/
public function store(Request $request)
{
$data = $request->all();
- $todo = new Todo;
- $todo->name = $data['name'];
- $todo->save();
+ $user = Auth::user();
+ $user->todos()->create([
+ 'name' => $data['name'],
+ ]);
}
save 或是 create 方法都有批量新增的版本,可以一次建立多筆關聯資料
$user->todos()->saveMany([
new Todo(['name' => 'todo 1']),
new Todo(['name' => 'todo 2']),
]);
$user->todos()->createMany([
['name' => 'todo 1'],
['name' => 'todo 2'],
]);
讀取的方式跟一對一關聯時相同
$user->todos
所以來改寫 TodoController 的 index 方法,變成只回傳登入用戶的 todos
@@ -13,11 +14,9 @@ class TodoController extends Controller
* @return \Illuminate\Http\Response
*/
public function index()
- {
- $todos = Todo::get();
-
+ {
return inertia('Dashboard', [
- 'todos' => $todos,
+ 'todos' => Auth::user()->todos,
]);
}
這樣就好了,可以試試不同帳號的畫面上會顯示不同的 todo 清單。
目前介紹了一對一,一對多的建立關聯資料方法,一般使用情況下關聯資料建立後就不會再變更所屬了,最多就是編輯資料。
不過偶爾會有需要移除關聯或是改變所屬的狀況,這邊先介紹一對一跟一對多情況的寫法,兩者是相同的,之後多對多的寫法另外介紹。
移除關聯表示將資料中的外鍵清除,這樣查詢關聯時就不會再查到他了,不過資料本身還是保留的。
首先要注意如果允許資料可以沒有關聯的話,在 Migration 時要將外鍵設定成 nullable,不然清除外鍵的時候會牴觸資料庫設定然後報錯。
$table->unsignedBigInteger('user_id')->nullable(); //允許沒有關聯
$table->foreign('user_id')->references('id')->on('users');
然後,會由 belongsTo 的那一方發起移除關聯,使用的是 dissociate 方法。
$todo->user()->dissociate();
$todo->save();
因為一對一跟一對多關聯中從屬的那一方都是用 belongsTo 方法,所以兩者都可以用 dissociate 移除關聯。
跟移除關聯相同,變更關聯也是由 belongsTo 那一方的 Model 發起變更,使用 associate 方法。
$todo = Todo::find(1);
$todo->user()->associate($user);
$todo->save();
不管 todo 原先有沒有所屬都會被更新所屬的 user 。